/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.replica.replicaisland;

import com.replica.replicaisland.ChannelSystem.Channel;
import com.replica.replicaisland.GameObject.ActionType;

public class NPCAnimationComponent extends GameComponent {

	// Animations
	public static final int IDLE = 0;
	public static final int WALK = 1;
	public static final int RUN_START = 2;
	public static final int RUN = 3;
	public static final int SHOOT = 4;
	public static final int JUMP_START = 5;
	public static final int JUMP_AIR = 6;
	public static final int TAKE_HIT = 7;
	public static final int SURPRISED = 8;
	public static final int DEATH = 9;

	protected static final float RUN_SPEED_THRESHOLD = 100.0f;
	protected static final float JUMP_SPEED_THRESHOLD = 25.0f;
	protected static final float FALL_SPEED_THRESHOLD = -25.0f;
	protected static final float FALL_TIME_THRESHOLD = 0.2f;

	private int mCurrentAnimation;
	private SpriteComponent mSprite;
	private Channel mChannel;
	private int mChannelTrigger;
	private boolean mFlying;
	private boolean mStopAtWalls; // Controls whether or not the character will
									// go back
									// to idle when running into a wall

	public NPCAnimationComponent() {
		super();
		reset();
		setPhase(ComponentPhases.ANIMATION.ordinal());
	}

	@Override
	public void reset() {
		mCurrentAnimation = IDLE;
		mChannel = null;
		mSprite = null;
		mFlying = false;
		mStopAtWalls = true;
	}

	@Override
	public void update(float timeDelta, BaseObject parent) {
		if (mSprite != null) {
			GameObject parentObject = (GameObject) parent;

			final int oldAnimation = mCurrentAnimation;
			switch (mCurrentAnimation) {
			case IDLE:
				idle(parentObject);
				break;
			case WALK:
				walk(parentObject);
				break;
			case RUN_START:
				runStart(parentObject);
				break;
			case RUN:
				run(parentObject);
				break;
			case SHOOT:
				shoot(parentObject);
				break;
			case JUMP_START:
				jumpStart(parentObject);
				break;
			case JUMP_AIR:
				jumpAir(parentObject);
				break;
			case TAKE_HIT:
				takeHit(parentObject);
				break;
			case SURPRISED:
				surprised(parentObject);
				break;
			case DEATH:
				death(parentObject);
				break;
			default:
				assert (false);
			}

			if (mChannel != null) {
				if (mChannel.value != null
						&& ((ChannelSystem.ChannelBooleanValue) mChannel.value).value) {
					mCurrentAnimation = mChannelTrigger;
				}
			}

			if (oldAnimation != mCurrentAnimation) {
				mSprite.playAnimation(mCurrentAnimation);
			}
		}
	}

	protected boolean shouldFall(GameObject parentObject) {
		boolean result = false;
		TimeSystem time = sSystemRegistry.timeSystem;
		final float airTime = time.getGameTime()
				- parentObject.getLastTouchedFloorTime();
		if (!mFlying && !parentObject.touchingGround()
				&& airTime > FALL_TIME_THRESHOLD) {
			final Vector2 velocity = parentObject.getVelocity();
			if (velocity.y < FALL_SPEED_THRESHOLD) {
				result = true;
			}
		}
		return result;
	}

	protected boolean shouldJump(GameObject parentObject) {
		boolean result = false;

		if (!mFlying) {
			final Vector2 velocity = parentObject.getVelocity();
			if (velocity.y > JUMP_SPEED_THRESHOLD) {
				result = true;
			}
		}
		return result;
	}

	protected boolean shouldRun(GameObject parentObject) {
		boolean result = false;
		if (!mFlying && parentObject.touchingGround()) {
			final Vector2 velocity = parentObject.getVelocity();
			if (Math.abs(velocity.x) >= RUN_SPEED_THRESHOLD) {
				result = true;
			}
		}
		return result;
	}

	protected boolean shouldMove(GameObject parentObject) {
		boolean result = true;
		final Vector2 velocity = parentObject.getVelocity();

		if (mStopAtWalls) {
			if ((velocity.x < 0.0f && parentObject.touchingLeftWall())
					|| (velocity.x > 0.0f && parentObject.touchingRightWall())) {
				result = false;
			}
		}
		return result;
	}

	protected boolean shouldTakeHit(GameObject parentObject) {
		boolean result = false;
		if (parentObject.getCurrentAction() == ActionType.HIT_REACT
				&& mSprite.findAnimation(TAKE_HIT) != null) {
			result = true;
		}
		return result;
	}

	protected void gotoRunStart() {
		if (mSprite.findAnimation(RUN_START) != null) {
			mCurrentAnimation = RUN_START;
		} else {
			mCurrentAnimation = RUN;
		}
	}

	protected void gotoRun() {
		mCurrentAnimation = RUN;
	}

	protected void idle(GameObject parentObject) {
		final ActionType currentAction = parentObject
				.getCurrentAction();
		if (currentAction == ActionType.MOVE) {
			final Vector2 velocity = parentObject.getVelocity();
			if (shouldFall(parentObject)) {
				mCurrentAnimation = JUMP_AIR;
			} else if (shouldJump(parentObject)) {
				mCurrentAnimation = JUMP_START;
				parentObject.positionLocked = true;
			} else if (Math.abs(velocity.x) > 0.0f && shouldMove(parentObject)) {
				if (shouldRun(parentObject)) {
					gotoRunStart();
					parentObject.positionLocked = true;
				} else {
					mCurrentAnimation = WALK;
				}
			}
		} else if (currentAction == ActionType.ATTACK) {
			mCurrentAnimation = SHOOT;
		} else if (shouldTakeHit(parentObject)) {
			mCurrentAnimation = TAKE_HIT;
		} else if (parentObject.getCurrentAction() == ActionType.DEATH) {
			mCurrentAnimation = DEATH;
		}
	}

	protected void walk(GameObject parentObject) {
		final ActionType currentAction = parentObject
				.getCurrentAction();
		if (currentAction == ActionType.MOVE) {
			final Vector2 velocity = parentObject.getVelocity();
			if (shouldFall(parentObject)) {
				mCurrentAnimation = JUMP_AIR;
			} else if (shouldJump(parentObject)) {
				mCurrentAnimation = JUMP_START;
				parentObject.positionLocked = true;
			} else if (Math.abs(velocity.x) > 0.0f) {
				if (shouldRun(parentObject)) {
					gotoRun();
				}
				if (velocity.x > 0.0f) {
					parentObject.facingDirection.x = 1;
				} else {
					parentObject.facingDirection.x = -1;
				}
			} else {
				mCurrentAnimation = IDLE;
			}
		} else if (currentAction == ActionType.ATTACK) {
			mCurrentAnimation = SHOOT;
		} else if (shouldTakeHit(parentObject)) {
			mCurrentAnimation = TAKE_HIT;
		} else if (parentObject.getCurrentAction() == ActionType.DEATH) {
			mCurrentAnimation = DEATH;
		}
	}

	protected void runStart(GameObject parentObject) {
		parentObject.positionLocked = true;
		if (mSprite.animationFinished()) {
			mCurrentAnimation = RUN;
			parentObject.positionLocked = false;
		}
	}

	protected void run(GameObject parentObject) {
		final ActionType currentAction = parentObject
				.getCurrentAction();
		if (currentAction == ActionType.MOVE) {
			final Vector2 velocity = parentObject.getVelocity();
			if (shouldFall(parentObject)) {
				mCurrentAnimation = JUMP_AIR;
			} else if (shouldJump(parentObject)) {
				parentObject.positionLocked = true;
				mCurrentAnimation = JUMP_START;
			} else if (Math.abs(velocity.x) > 0.0f) {
				if (!shouldRun(parentObject)) {
					mCurrentAnimation = WALK;
				}

				if (velocity.x > 0.0f) {
					parentObject.facingDirection.x = 1;
				} else {
					parentObject.facingDirection.x = -1;
				}
			} else {
				mCurrentAnimation = IDLE;
			}
		} else if (currentAction == ActionType.ATTACK) {
			mCurrentAnimation = SHOOT;
		} else if (shouldTakeHit(parentObject)) {
			mCurrentAnimation = TAKE_HIT;
		} else if (parentObject.getCurrentAction() == ActionType.DEATH) {
			mCurrentAnimation = DEATH;
		}
	}

	protected void shoot(GameObject parentObject) {
		if (mSprite.animationFinished()
				|| parentObject.getCurrentAction() != ActionType.ATTACK) {
			mCurrentAnimation = IDLE;
		} else if (shouldTakeHit(parentObject)) {
			mCurrentAnimation = TAKE_HIT;
		} else if (parentObject.getCurrentAction() == ActionType.DEATH) {
			mCurrentAnimation = DEATH;
		} else {
			final Vector2 velocity = parentObject.getVelocity();

			if (velocity.x > 0.0f) {
				parentObject.facingDirection.x = 1;
			} else if (velocity.x < 0.0f) {
				parentObject.facingDirection.x = -1;
			}
		}
	}

	protected void jumpStart(GameObject parentObject) {
		final Vector2 velocity = parentObject.getVelocity();

		if (velocity.x > 0.0f) {
			parentObject.facingDirection.x = 1;
		} else if (velocity.x < 0.0f) {
			parentObject.facingDirection.x = -1;
		}
		parentObject.positionLocked = true;

		if (mSprite.animationFinished()) {
			mCurrentAnimation = JUMP_AIR;
			parentObject.positionLocked = false;
		}
	}

	protected void jumpAir(GameObject parentObject) {
		final ActionType currentAction = parentObject
				.getCurrentAction();
		if (currentAction == ActionType.MOVE) {
			final Vector2 velocity = parentObject.getVelocity();

			if (parentObject.touchingGround()) {
				if (Math.abs(velocity.x) > 0.0f) {
					if (shouldRun(parentObject)) {
						mCurrentAnimation = RUN;
					} else {
						mCurrentAnimation = WALK;
					}
				} else {
					mCurrentAnimation = IDLE;
				}
			} else {

				if (velocity.x > 0.0f) {
					parentObject.facingDirection.x = 1;
				} else if (velocity.x < 0.0f) {
					parentObject.facingDirection.x = -1;
				}

			}
		} else {
			mCurrentAnimation = IDLE;
		}
	}

	protected void takeHit(GameObject parentObject) {
		if (mSprite.animationFinished()) {
			if (parentObject.life > 0
					&& parentObject.getCurrentAction() != ActionType.DEATH) {
				if (parentObject.getCurrentAction() != ActionType.HIT_REACT) {
					mCurrentAnimation = IDLE;
				}
			} else {
				mCurrentAnimation = DEATH;
			}
		}
	}

	protected void surprised(GameObject parentObject) {
		if (mSprite.animationFinished()) {
			mCurrentAnimation = IDLE;
		}
	}

	protected void death(GameObject parentObject) {
	}

	public void setSprite(SpriteComponent sprite) {
		mSprite = sprite;
	}

	public void setChannel(Channel channel) {
		mChannel = channel;
	}

	public void setChannelTrigger(int animation) {
		mChannelTrigger = animation;
	}

	public void setFlying(boolean flying) {
		mFlying = flying;
	}

	public void setStopAtWalls(boolean stop) {
		mStopAtWalls = stop;
	}
}
